package com.yunchao.library.lock

import com.yunchao.library.lock.IDeviceHelper.Companion.PSAM_AUTH_ERROR_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.PSAM_CREATE_CMD_ERROR_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.PSAM_DECRYPTION_ERROR_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.PSAM_GET_PASSWORD_ERROR_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.PSAM_SUCCEED_INFO
import com.yunchao.library.lock.IDeviceHelper.Companion.RFID_READ_LOCK_LOG_LEN_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.RFID_READ_LOCK_LOG_MESSAGE
import com.yunchao.library.lock.IDeviceHelper.Companion.RFID_WRITE_LOCK_CMD_MESSAGE
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.withContext
import timber.log.Timber

abstract class DeviceHelper : IDeviceHelper {

    protected val psamMutex: Mutex by lazy { Mutex() }


    /**
     * 锁日志总数
     */
    override suspend fun readLockLogCount(lockEPCInfo: LockEPCInfo): Int =
        withContext(Dispatchers.Default) {
            val result = readTagData(epcId = lockEPCInfo.epc, offset = 73 * 2, len = 2)
            result.toInt(radix = 16)
        }


    /**
     * 读取锁物流日志
     */
    override suspend fun readLockLogisticsLog(): Pair<LockEPCInfo, List<String>> =
        withContext(Dispatchers.Default) {
            val lockEPCInfo = async { inventoryOnce() }.await()
            val data = readTagData(epcId = lockEPCInfo.epc, offset = 0, len = 128)
            lockEPCInfo to data.chunked(32).filterNot { it.startsWith("00") }
        }

    suspend fun readLockLogisticsLog(lockEPCInfo: LockEPCInfo): List<String> =
        withContext(Dispatchers.Default) {
            val data = readTagData(epcId = lockEPCInfo.epc, offset = 0, len = 128)
            data.chunked(32).filterNot { it.startsWith("00") }
        }


    /**
     * 读取锁日志
     */
    override suspend fun readLockLog(
        lockEPCInfo: LockEPCInfo,
        start: Int,
        number: Int
    ): Pair<LockEPCInfo, List<String>> = withContext(Dispatchers.Default) {
        check(authPSAM()) { PSAM_AUTH_ERROR_MESSAGE }

        val lockCmdResult = processedCmdByPSAM(getLockLogCmdStr(lockEPCInfo.epc, start, number))
        check(lockCmdResult.endsWith(PSAM_SUCCEED_INFO)) { PSAM_CREATE_CMD_ERROR_MESSAGE }

        val lockPassword = processedCmdByPSAM(getReadWritePwd(lockEPCInfo.epc)).substring(0, 8)

        val writeResult = writeLockCmd(lockEPCInfo, lockPassword, lockCmdResult.dropLast(4))

        check(writeResult) { RFID_WRITE_LOCK_CMD_MESSAGE }

        readLockLogResult(lockEPCInfo.epc, lockPassword)
    }

    /**
     * 读取锁日志
     */
    private suspend fun readLockLogResult(
        epcId: String,
        password: String
    ): Pair<LockEPCInfo, List<String>> =
        retryCoroutine {
            val lockEPCInfo = verifyLockStatus(epcId, password, LockAction.LOCK_LOG)

            val logLenHexStr =
                readTagData(epcId = lockEPCInfo.epc, offset = 76 * 2, len = 2, password = password)

            check(logLenHexStr.startsWith("BB")) { RFID_READ_LOCK_LOG_LEN_MESSAGE }

            val logLen = logLenHexStr.substring(2).toInt(radix = 16) + 2
            Timber.i("锁日志长度: $logLen")
            val lockLogInfo = readTagData(
                epcId = lockEPCInfo.epc,
                offset = 76 * 2,
                len = logLen,
                password = password
            )

            check(lockLogInfo.length > 4) { RFID_READ_LOCK_LOG_MESSAGE }

            val lockLog = processedCmdByPSAM(getEncryptCmd(epcId, lockLogInfo.substring(4)))

            check(lockLog.endsWith(PSAM_SUCCEED_INFO)) { PSAM_DECRYPTION_ERROR_MESSAGE }

            lockEPCInfo to lockLog.dropLast(4).chunked(48).filterNot { it.startsWith("00") }
        }

    /**
     * 处理锁指令 并验证锁状态
     */
    override suspend fun handleLockCommand(
        lockEPCInfo: LockEPCInfo,
        action: LockAction,
        deviceId: Int,
        operatorId: Int,
        chkUserId: Int,
        pageNumber: Int,
        voucherType: String
    ): LockEPCInfo = withContext(Dispatchers.Default) {
        Timber.d(
            """
                执行开闭锁、恢复锁、清空锁日志传入的参数：
                
                |锁信息: $lockEPCInfo
                |操作: $action
                |设备ID: $deviceId
                |操作员ID: $operatorId
                |校验员ID: $chkUserId
                |页码: $pageNumber
                |凭证类型: $voucherType            
        """.trimIndent()
        )
        //验证PSAM
        check(authPSAM()) { PSAM_AUTH_ERROR_MESSAGE }

        //生成指令
        val lockCmdResult = processedCmdByPSAM(
            action.getLockCmd(
                lockEPCInfo,
                deviceId,
                operatorId,
                chkUserId,
                pageNumber,
                voucherType
            )
        )

        Timber.d("锁指令: $lockCmdResult")

        check(lockCmdResult.endsWith(PSAM_SUCCEED_INFO)) { "$PSAM_CREATE_CMD_ERROR_MESSAGE : $lockCmdResult" }

        //生成密钥
        val lockPassword = processedCmdByPSAM(getReadWritePwd(lockEPCInfo.epc))

        Timber.d("锁密码: $lockPassword")

        check(lockPassword.length >= 8 && lockPassword.endsWith(PSAM_SUCCEED_INFO)) { "$PSAM_GET_PASSWORD_ERROR_MESSAGE : $lockPassword" }

        //给锁写指令
        val writeResult = writeLockCmd(
            lockEPCInfo = lockEPCInfo,
            lockPassword = lockPassword.substring(0, 8),
            lockCmdResult = lockCmdResult.dropLast(4)
        )

        Timber.d("写指令结果: $writeResult")

        check(writeResult) { RFID_WRITE_LOCK_CMD_MESSAGE }


        //验证锁信息
        retryCoroutine {
            verifyLockStatus(
                epcId = lockEPCInfo.epc,
                password = lockPassword.substring(0, 8),
                action = action
            )
        }
    }


    /**
     * 给锁写入指令
     */
    private suspend fun writeLockCmd(
        lockEPCInfo: LockEPCInfo,
        lockPassword: String,
        lockCmdResult: String,
        offset: Int = 76 * 2
    ): Boolean {
        val strCmd2User = "66${"%02x".format(lockCmdResult.length / 2)}"
        val cmdStr =
            "${strCmd2User}${lockCmdResult}${if (strCmd2User.length % 4 != 0) "00" else ""}"
        return writeTagData(
            epcId = lockEPCInfo.epc,
            password = lockPassword,
            offset = offset,
            len = cmdStr.length / 2,
            data = cmdStr
        )
    }


    /**
     * PSAM 生成指令
     */
    suspend fun processedCmdByPSAM(data: String): String {
        val response = sendApdu(data = data)
        val responseBytes = response.hexStringToByteArray()
        return when {
            response.endsWith(PSAM_SUCCEED_INFO) -> response
            response.startsWith("61") -> {
                val cmd =
                    "00C0000000".hexStringToByteArray().apply { this[lastIndex] = responseBytes[1] }
                sendApdu(data = cmd.byteArrayToHexString())
            }

            response.startsWith("6C", true) -> {
                val bytes = data.hexStringToByteArray()
                bytes[bytes.size - 1] = responseBytes[1]
                sendApdu(data = bytes.byteArrayToHexString())
            }

            else -> response
        }
    }

}